from base import *
from aps.models.item import *
from aps.models.fase import *
from aps.models.proyecto import *
from aps.models.lineabase import *

class Grafo(object):
    """
    Clase que maneja el grafo del proyecto
    """
    idproyecto = 0
    nodos      = {}
    
    def __init__(self,idproyecto):
        """
        Metodo de instancia constructor que inicializa los parametros del objeto grafo
        @type self: Grafo
        @param self: referencia al objeto que llama el metodo en este caso grafo.
        @type idproyecto: Integer
        @param idproyecto: identificador del proyecto que requiere una construccion del grafo.
        """
        self.idproyecto = idproyecto
        proyecto = bdsession.query(Proyecto).filter_by(id=idproyecto).first()
        # Procesamos las fases del proyecto
        for f in proyecto.fases:
            items = bdsession.query(Item).filter_by(idfase=f.fase.id).all()
            # Procesamos los items de cada fase
            for i in items:
                p = [ x.idorigen for x in i.get_antecesores()['validas'] ]
                s = [ x.iddestino for x in i.get_sucesores()['validas'] ]
                # p : nodos predecesores
                # s : nodos sucesores
                # v : marca que indica si el nodo ya fue visitado
                # f : id de la fase a la que pertenece
                self.nodos[i.id] = {'p':p,'s':s,'v':False,'f':i.idfase}
    
    def marcar(self,marca=False):
        """
        Metodo que marca todos los nodos del grafo
        @type self: Grafo
        @param self: referencia al objeto que llama a este metodo.
        @type marca: Bool
        @param marca: marca que se aplica a todos los nodos, puede ser False o True.
        """
        for n in self.nodos:
            self.nodos[n]['v'] = marca
    
    def _buscar_caminos(self,nodos,origen,destino,camino=[]):
        """
        Metodo que busca todos los caminos entre un vertice de origen y otro de destino
        @type self: grafo
        @param self: referencia al objeto que llama el metodo en este caso grafo.
        @type nodos: Dictionary
        @param nodos: lista de nodos del proyecto.
        @type origen: Integer
        @param origen: Nodo de origen desde donde buscar.
        @type destino: Integer
        @param destino: Nodo de destino hasta donde buscar.
        @type camino: List
        @param camino: Caminos encontrados
        """
        camino = camino + [origen]
        if origen == destino:
            return [camino]
        if not nodos.has_key(origen):
            return []
        caminos = []
        for node in nodos[origen]['s']:
            if node not in camino:
                new_caminos = self._buscar_caminos(nodos, node, destino, camino)
                for new_camino in new_caminos:
                    caminos.append(new_camino)
        return caminos
    
    def buscar_caminos(self,origen,destino,camino=[]):
        """
        Metodo que enmascara al metodo principal de busqueda de caminos
        @type self: grafo
        @param self: referencia al objeto que llama el metodo en este caso grafo.
        @type origen: Integer
        @param origen: Nodo de origen desde donde buscar.
        @type destino: Integer
        @param destino: Nodo de destino hasta donde buscar.
        @type camino: List
        @param camino: Caminos encontrados
        """
        return self._buscar_caminos(self.nodos,origen,destino,camino)
    
    def impacto_hacia_adelante(self,iditem):
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        # suma total de las complejidades
        total = 0
        # creamos una cola y agregamos el item inicial
        cola  = []
        self.nodos[iditem]['v'] = True
        cola.append(iditem)
        while len(cola) != 0:
            # obtenemos un item en el camino
            id    = cola.pop(0)
            item  = self.nodos[id]
            total = total + bdsession.query(Item).filter_by(id=id).first().complejidad
            # recorremos los sucesores del item
            for s in item['s']:
                if self.nodos[s]['v'] == False:
                    # si el nodo no fue visitado, se marca y se encola
                    self.nodos[s]['v'] = True
                    cola.append(s)
        return total - bdsession.query(Item).filter_by(id=iditem).first().complejidad

    def prioridad_hacia_adelante(self,iditem):
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        # suma total de las complejidades
        total = 0
        # creamos una cola y agregamos el item inicial
        cola  = []
        self.nodos[iditem]['v'] = True
        cola.append(iditem)
        while len(cola) != 0:
            # obtenemos un item en el camino
            id    = cola.pop(0)
            item  = self.nodos[id]
            total = total + bdsession.query(Item).filter_by(id=id).first().prioridad
            # recorremos los sucesores del item
            for s in item['s']:
                if self.nodos[s]['v'] == False:
                    # si el nodo no fue visitado, se marca y se encola
                    self.nodos[s]['v'] = True
                    cola.append(s)
        return total - bdsession.query(Item).filter_by(id=iditem).first().prioridad
    
        
    def impacto_hacia_atras(self,iditem):
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        # suma total de las complejidades
        total = 0
        # creamos una cola y agregamos el item inicial
        cola  = []
        self.nodos[iditem]['v'] = True
        cola.append(iditem)
        while len(cola) != 0:
            # obtenemos un item en el camino
            id    = cola.pop(0)
            item  = self.nodos[id]
            total = total + bdsession.query(Item).filter_by(id=id).first().complejidad
            # recorremos los predecesores del item
            for s in item['p']:
                if self.nodos[s]['v'] == False:
                    # si el nodo no fue visitado, se marca y se encola
                    self.nodos[s]['v'] = True
                    cola.append(s)
        return total - bdsession.query(Item).filter_by(id=iditem).first().complejidad

    def prioridad_hacia_atras(self,iditem):
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        # suma total de las complejidades
        total = 0
        # creamos una cola y agregamos el item inicial
        cola  = []
        self.nodos[iditem]['v'] = True
        cola.append(iditem)
        while len(cola) != 0:
            # obtenemos un item en el camino
            id    = cola.pop(0)
            item  = self.nodos[id]
            total = total + bdsession.query(Item).filter_by(id=id).first().prioridad
            # recorremos los predecesores del item
            for s in item['p']:
                if self.nodos[s]['v'] == False:
                    # si el nodo no fue visitado, se marca y se encola
                    self.nodos[s]['v'] = True
                    cola.append(s)
        return total - bdsession.query(Item).filter_by(id=iditem).first().prioridad

    
    def marcar_revision_sucesores(self,iditem):
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        # creamos una cola y agregamos el item inicial
        cola  = []
        self.nodos[iditem]['v'] = True
        cola.append(iditem)
        while len(cola) != 0:
            # obtenemos un item en el camino
            id    = cola.pop(0)
            item  = self.nodos[id]
            it = bdsession.query(Item).filter_by(id=id).first()
            # cambiamos el estado y el idlineabase
            it.estado = 'REVISION'
            #cambiamos el estado de la linea base a abierta
            lb = None
            lb = bdsession.query(Lineabase).filter_by(id=it.idlineabase).first()
            if lb is not None:
                lb.estado = "Abierta"
            
            
            it.idlineabase = None
            bdsession.merge(it)
            # recorremos los sucesores del item
            for s in item['s']:
                if self.nodos[s]['v'] == False:
                    # si el nodo no fue visitado, se marca y se encola
                    self.nodos[s]['v'] = True
                    cola.append(s)
    
    def cluster_adelante_data(self,iditem,items=[]):
        if len(items) == 0 and iditem is not None:
            cola = [iditem]
        elif len(items) != 0 and iditem is None:
            cola = items
        else:
            return None
        
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        
        # datos utilizados para obtener los valores
        nant  = []
        rclu  = ''
        rant  = ''
        nodos = ''
        fase  = self.nodos[cola[0]]['f']
        mfase = bdsession.query(Fase).filter_by(id=fase).first()
        
        # recorremos la cola
        while len(cola) != 0:
            id         = cola.pop()
            relaciones = self.nodos[id]
            origen     = bdsession.query(Item).filter_by(id=id).first()
            # controlamos que sea de la misma fase
            if self.nodos[id]['f'] == fase:
                nodos  += '{0} [label="{1}({2})",style=filled,fillcolor=skyblue3,penwidth=2]\n'.format(origen.id,origen.nombre,origen.complejidad)
            # recorremos la lista de sucesores del item
            for sucesor in relaciones['s']:
                # controlamos que el nodo no este visitado
                if self.nodos[sucesor]['v'] == False:
                    destino  = bdsession.query(Item).filter_by(id=sucesor).first()
                    relacion = bdsession.query(Relacion).filter_by(idorigen=origen.id,vorigen=origen.version,iddestino=destino.id,vdestino=destino.version).first()
                    # deteminamos el tipo de la relacion
                    if relacion.tipo == 'AS':
                        # controlamos que pertenezca a la fase siguiente
                        if self.nodos[sucesor]['f'] == fase+1:
                            rant += '{0} -- {1};\n'.format(origen.id,destino.id)
                            nant.append(destino.id)
                    else:
                        # controlamos que sea en la misma fase
                        if self.nodos[sucesor]['f'] == fase:
                            rclu += '{0} -- {1};\n'.format(origen.id,destino.id)
                    # marcamos el nodo y agregamos a la cola
                    self.nodos[sucesor]['v'] = True
                    cola.append(sucesor)
        
        return {'fase':mfase.nombre,'nodos':nodos,'cluster':rclu[:-1],'c2c':rant[:-1],'items':nant}
    
    def cluster_atras_data(self,iditem,items=[]):
        if len(items) == 0 and iditem is not None:
            cola = [iditem]
        elif len(items) != 0 and iditem is None:
            cola = items
        else:
            return None
        
        # marcamos todos los nodos como no visitados
        self.marcar(False)
        
        # datos utilizados para obtener los valores
        nant  = []
        rclu  = ''
        rant  = ''
        nodos = ''
        fase  = self.nodos[cola[0]]['f']
        mfase = bdsession.query(Fase).filter_by(id=fase).first()
        
        # recorremos la cola
        while len(cola) != 0:
            id         = cola.pop()
            relaciones = self.nodos[id]
            destino    = bdsession.query(Item).filter_by(id=id).first()
            # controlamos que sea de la misma fase
            if self.nodos[id]['f'] == fase:
                nodos  += '{0} [label="{1}({2})",style=filled,fillcolor=skyblue3,penwidth=2]\n'.format(destino.id,destino.nombre,destino.complejidad)
                print nodos
            # recorremos la lista de predecesores del item
            for sucesor in relaciones['p']:
                # controlamos que el nodo no este visitado
                if self.nodos[sucesor]['v'] == False:
                    origen   = bdsession.query(Item).filter_by(id=sucesor).first()
                    relacion = bdsession.query(Relacion).filter_by(idorigen=origen.id,vorigen=origen.version,iddestino=destino.id,vdestino=destino.version).first()
                    # deteminamos el tipo de la relacion
                    if relacion.tipo == 'AS':
                        # controlamos que pertenezca a la fase anterior
                        if self.nodos[sucesor]['f'] == fase-1:
                            rant += '{0} -- {1};\n'.format(origen.id,destino.id)
                            nant.append(origen.id)
                            
                    else:
                        # controlamos que sea en la misma fase
                        if self.nodos[sucesor]['f'] == fase:
                            rclu += '{0} -- {1};\n'.format(origen.id,destino.id)
                           
                    # marcamos el nodo y agregamos a la cola
                    self.nodos[sucesor]['v'] = True
                    cola.append(sucesor)
        
        return {'fase':mfase.nombre,'nodos':nodos, 'cluster':rclu, 'c2c':rant, 'items':nant}

